# -*- coding:binary -*-
require 'spec_helper'

require 'msf/core'
require 'msf/core/data_store'
require 'msf/core/exploit/http/client'

RSpec.describe Msf::Exploit::Remote::HttpClient do
  subject do
    mod = ::Msf::Module.new
    mod.extend described_class
    mod
  end

  describe '#reconfig_redirect_opts!' do
    context 'when URI is http://127.0.0.1/test/redirect.php' do
      it 'should return /test/redirect.php as the URI path' do
        res = Rex::Proto::Http::Response.new
        allow(res).to receive(:headers).and_return({'Location'=>'http://127.0.0.1/test/redirect.php'})
        opts = {}
        subject.reconfig_redirect_opts!(res, opts)
        expect(opts['uri']).to eq('/test/redirect.php')
      end
    end

    context 'when URI is /test/redirect.php' do
      it 'should return /test/redirect.php' do
        res = Rex::Proto::Http::Response.new
        allow(res).to receive(:headers).and_return({'Location'=>'/test/redirect.php'})
        opts = {}
        subject.reconfig_redirect_opts!(res, opts)
        expect(opts['uri']).to eq('/test/redirect.php')
      end
    end

    context 'when URI is ./redirect.php' do
      it 'should return /redirect.php' do
        res = Rex::Proto::Http::Response.new
        allow(res).to receive(:headers).and_return({'Location'=>'./redirect.php'})
        opts = {}
        subject.reconfig_redirect_opts!(res, opts)
        expect(opts['uri']).to eq('/redirect.php')
      end
    end
  end

  describe '#vhost' do

    let(:rhost) do
      'rhost.example.com'
    end

    let(:vhost) do
      'vhost.example.com'
    end

    context 'when vhost is defined' do
      let(:cli_vhost) do
        subject.datastore['VHOST'] = vhost
        subject
      end
      it "should return the set vhost" do
        expect(cli_vhost.vhost).to eq vhost
      end
    end

    context 'when only rhost is defined' do
      let(:cli_rhost) do
        subject.datastore['RHOST'] = rhost
        subject
      end
      it "should return the rhost as the vhost" do
        expect(cli_rhost.datastore['VHOST']).to be_nil
        expect(cli_rhost.vhost).to eq rhost
      end
    end

  end

  describe '#normalize_uri' do
    let(:expected_normalized_uri) do
      '/a/b/c'
    end

    let(:normalized_uri) do
      subject.normalize_uri(unnormalized_uri)
    end

    context "with just '/'" do
      let(:unnormalized_uri) do
        '/'
      end

      it "should be '/'" do
        expect(unnormalized_uri).to eq '/'
      end

      it "should return '/'" do
        expect(normalized_uri).to eq '/'
      end
    end

    context "with starting '/'" do
      let(:unnormalized_uri) do
        expected_normalized_uri
      end

      it "should start with '/'" do
        expect(unnormalized_uri[0, 1]).to eq '/'
      end

      it "should not add another starting '/'" do
        expect(normalized_uri).to eq expected_normalized_uri
      end

      context "with multiple internal '/'" do
        let(:unnormalized_uri) do
          "/#{expected_normalized_uri.gsub("/", "////")}"
        end

        it "should remove doubled internal '/'" do
          expect(normalized_uri).to eq expected_normalized_uri
        end
      end

      context "with multiple starting '/'" do
        let(:unnormalized_uri) do
          "/#{expected_normalized_uri}"
        end

        it "should have at least 2 starting '/'" do
          expect(unnormalized_uri[0, 2]).to eq '//'
        end

        it "should return with one starting '/'" do
          expect(normalized_uri).to eq expected_normalized_uri
        end
      end

      context "with trailing '/'" do
        let(:expected_normalized_uri) do
          '/a/b/c/'
        end

        let(:unnormalized_uri) do
          "#{expected_normalized_uri}/"
        end

        it "should end with '/'" do
          expect(normalized_uri[-1, 1]).to eq '/'
        end

        context "with multiple trailing '/'" do
          let(:unnormalized_uri) do
            "#{expected_normalized_uri}/"
          end

          it "should have multiple trailing '/'" do
            expect(unnormalized_uri[-2,2]).to eq '//'
          end

          it "should return only one trailing '/'" do
            expect(normalized_uri).to eq expected_normalized_uri
          end
        end
      end

      context "without trailing '/'" do
        let(:unnormalized_uri) do
          expected_normalized_uri
        end

        it "should not have a trailing '/'" do
          expect(unnormalized_uri[-1, 1]).not_to eq '/'
        end

        it "should return original string" do
          expect(normalized_uri).to eq expected_normalized_uri
        end
      end
    end

    context "without starting '/'" do
      context "with trailing '/'" do
        let(:unnormalized_uri) do
          'a/b/c/'
        end
        let(:expected_normalized_uri) do
          '/a/b/c/'
        end

        it "should have trailing '/'" do
          expect(unnormalized_uri[-1, 1]).to eq '/'
        end

        it "should add starting '/'" do
          expect(normalized_uri[0, 1]).to eq '/'
        end

        it "should not remove trailing '/'" do
          expect(normalized_uri[-1, 1]).to eq '/'
        end

        it 'should normalize the uri' do
          expect(normalized_uri).to eq "#{expected_normalized_uri}"
        end

        context "with multiple internal '/'" do
          let(:unnormalized_uri) do
            "/#{expected_normalized_uri.gsub("/", "////")}"
          end

          it "should remove doubled internal '/'" do
            expect(normalized_uri).to eq expected_normalized_uri
          end
        end
      end

      context "without trailing '/'" do
        let(:unnormalized_uri) do
          'a/b/c'
        end

        it "should not have trailing '/'" do
          expect(unnormalized_uri[-1, 1]).not_to eq '/'
        end

        it "should add starting '/'" do
          expect(normalized_uri[0, 1]).to eq '/'
        end

        it "should add trailing '/'" do
          expect(normalized_uri[-1, 1]).not_to eq '/'
        end
      end
    end

    context 'with empty string' do
      let(:unnormalized_uri) do
        ''
      end

      it "should be empty" do
        expect(unnormalized_uri).to be_empty
      end

      it "should return '/'" do
        expect(normalized_uri).to eq '/'
      end
    end

    context 'with nil' do
      let(:unnormalized_uri) do
        nil
      end

      it 'should be nil' do
        expect(unnormalized_uri).to be_nil
      end

      it "should return '/" do
        expect(normalized_uri).to eq '/'
      end
    end
  end
end
